//! Type Index V2 - Core type definitions and indexing
//!
//! This module provides the new architecture for type discovery:
//! - Parse files with syn
//! - Skip `use` re-exports entirely (they are NOT definitions)
//! - Expand impl_vec!, impl_option!, impl_vec_debug! macros during indexing
//! - Build a fast lookup index

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::fs;
use std::sync::Arc;

use anyhow::Result;
use indexmap::IndexMap;
use rayon::prelude::*;
use syn::{File, Item, UseTree};
use quote::ToTokens;

// Re-export RefKind from the types module for use elsewhere
pub use crate::autofix::types::RefKind;

// ============================================================================
// DATA STRUCTURES
// ============================================================================

/// A type definition discovered from parsing source files.
/// This is NOT created for `use` re-exports - only actual definitions.
#[derive(Debug, Clone)]
pub struct TypeDefinition {
    /// Full path: "azul_core::resources::FontCache"
    pub full_path: String,
    /// Simple name: "FontCache"
    pub type_name: String,
    /// Source file where defined
    pub file_path: PathBuf,
    /// Module path within crate: "resources"
    pub module_path: String,
    /// Crate name: "azul_core"
    pub crate_name: String,
    /// Kind of type with all metadata
    pub kind: TypeDefKind,
    /// Source code of the definition
    pub source_code: String,
}

/// The kind of type definition
#[derive(Debug, Clone)]
pub enum TypeDefKind {
    Struct {
        fields: IndexMap<String, FieldDef>,
        /// The repr attribute value, e.g. Some("C") or None if missing
        repr: Option<String>,
        generic_params: Vec<String>,
        derives: Vec<String>,
        /// Traits with manual `impl Trait for Type` blocks (e.g., Clone, Debug, Drop)
        custom_impls: Vec<String>,
    },
    Enum {
        variants: IndexMap<String, VariantDef>,
        /// The repr attribute value, e.g. Some("C"), Some("C, u8"), or None if missing
        repr: Option<String>,
        generic_params: Vec<String>,
        derives: Vec<String>,
        /// Traits with manual `impl Trait for Type` blocks (e.g., Clone, Debug, Drop)
        custom_impls: Vec<String>,
    },
    TypeAlias {
        /// The base type (e.g., "CssPropertyValue" from "CssPropertyValue<BreakInside>")
        target: String,
        /// The generic base type without args (e.g., "CssPropertyValue")
        /// If None, this is a simple alias without generics
        generic_base: Option<String>,
        /// Generic arguments (e.g., ["BreakInside"])
        generic_args: Vec<String>,
    },
    CallbackTypedef {
        args: Vec<CallbackArg>,
        returns: Option<String>,
    },
    /// Type generated by a macro like impl_vec!
    MacroGenerated {
        source_macro: String,
        base_type: String,
        kind: MacroGeneratedKind,
    },
}

/// Kind of macro-generated type
#[derive(Debug, Clone)]
pub enum MacroGeneratedKind {
    /// FooVec from impl_vec!(Foo, FooVec, ...)
    Vec,
    /// FooVecDestructor from impl_vec!(Foo, FooVec, FooVecDestructor)
    VecDestructor,
    /// FooVecDestructorType - the callback_typedef for extern "C" fn(*mut FooVec)
    /// Generated from impl_vec! as the External variant's function pointer type
    VecDestructorType,
    /// OptionFoo from impl_option!(Foo, OptionFoo, ...)
    Option,
    /// OptionFooEnumWrapper from impl_option!(Foo, OptionFoo, OptionFooEnumWrapper)
    OptionEnumWrapper,
    /// ResultFooBar from impl_result!(Foo, Bar, ResultFooBar, ...)
    Result,
    /// CallbackWrapper from impl_callback!(CallbackWrapper, Option, CallbackValue, CallbackType)
    CallbackWrapper,
    /// CallbackValue from impl_callback!(CallbackWrapper, Option, CallbackValue, CallbackType)
    CallbackValue,
}

/// Field definition in a struct
#[derive(Debug, Clone)]
pub struct FieldDef {
    pub name: String,
    /// The base type (for pointers/Box, this is "c_void")
    pub ty: String,
    /// The reference kind (Value, ConstPtr, MutPtr, or Boxed)
    pub ref_kind: RefKind,
    pub doc: Vec<String>,
}

/// Variant definition in an enum
#[derive(Debug, Clone)]
pub struct VariantDef {
    pub name: String,
    pub ty: Option<String>,
    pub doc: Vec<String>,
}

/// Argument in a callback typedef
#[derive(Debug, Clone)]
pub struct CallbackArg {
    pub name: Option<String>,
    /// The base type without reference modifiers (e.g., "RefAny" not "&mut RefAny")
    pub ty: String,
    /// The reference kind (Ref, RefMut, Value)
    pub ref_kind: RefKind,
}

// ============================================================================
// MACRO EXPANSION
// ============================================================================

impl TypeDefinition {
    /// Expand a MacroGenerated type into a real Struct/Enum/CallbackTypedef.
    /// Returns the original kind if not MacroGenerated.
    pub fn expand_macro_generated(&self) -> TypeDefKind {
        match &self.kind {
            TypeDefKind::MacroGenerated { kind, base_type, source_macro } => {
                match kind {
                    MacroGeneratedKind::Vec => {
                        // impl_vec!(BaseType, VecType, DestructorType)
                        // VecType has: ptr, len, cap, destructor
                        // Destructor is named VecTypeDestructor (e.g. U8Vec -> U8VecDestructor)
                        let destructor_type = format!("{}Destructor", self.type_name);
                        let mut fields = IndexMap::new();
                        fields.insert("ptr".to_string(), FieldDef {
                            name: "ptr".to_string(),
                            ty: "c_void".to_string(),
                            ref_kind: RefKind::ConstPtr,
                            doc: Vec::new(),
                        });
                        fields.insert("len".to_string(), FieldDef {
                            name: "len".to_string(),
                            ty: "usize".to_string(),
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        fields.insert("cap".to_string(), FieldDef {
                            name: "cap".to_string(),
                            ty: "usize".to_string(),
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        fields.insert("destructor".to_string(), FieldDef {
                            name: "destructor".to_string(),
                            ty: destructor_type,
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        TypeDefKind::Struct {
                            fields,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::VecDestructor => {
                        // VecDestructor enum: DefaultRust, NoDestructor, External(DestructorTypeType)
                        let destructor_type_type = format!("{}Type", self.type_name);
                        let mut variants = IndexMap::new();
                        variants.insert("DefaultRust".to_string(), VariantDef {
                            name: "DefaultRust".to_string(),
                            ty: None,
                            doc: Vec::new(),
                        });
                        variants.insert("NoDestructor".to_string(), VariantDef {
                            name: "NoDestructor".to_string(),
                            ty: None,
                            doc: Vec::new(),
                        });
                        variants.insert("External".to_string(), VariantDef {
                            name: "External".to_string(),
                            ty: Some(destructor_type_type),
                            doc: Vec::new(),
                        });
                        TypeDefKind::Enum {
                            variants,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::VecDestructorType => {
                        // DestructorTypeType callback: extern "C" fn(*mut VecType)
                        // base_type here is the VecType name
                        TypeDefKind::CallbackTypedef {
                            args: vec![CallbackArg {
                                name: Some("ptr".to_string()),
                                ty: base_type.clone(),
                                ref_kind: RefKind::MutPtr,
                            }],
                            returns: None,
                        }
                    }
                    MacroGeneratedKind::Option => {
                        // impl_option!(BaseType, OptionType, ...)
                        // OptionType enum: None, Some(BaseType)
                        let mut variants = IndexMap::new();
                        variants.insert("None".to_string(), VariantDef {
                            name: "None".to_string(),
                            ty: None,
                            doc: Vec::new(),
                        });
                        variants.insert("Some".to_string(), VariantDef {
                            name: "Some".to_string(),
                            ty: Some(base_type.clone()),
                            doc: Vec::new(),
                        });
                        TypeDefKind::Enum {
                            variants,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string(), "Clone".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::OptionEnumWrapper => {
                        // OptionEnumWrapper: wraps the inner type with a tag
                        // Struct with: tag (u8), value (BaseType)
                        let mut fields = IndexMap::new();
                        fields.insert("tag".to_string(), FieldDef {
                            name: "tag".to_string(),
                            ty: "u8".to_string(),
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        fields.insert("payload".to_string(), FieldDef {
                            name: "payload".to_string(),
                            ty: base_type.clone(),
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        TypeDefKind::Struct {
                            fields,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string(), "Clone".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::Result => {
                        // impl_result!(OkType, ErrType, ResultType, ...)
                        // base_type is "Result<OkType, ErrType>"
                        // ResultType enum: Ok(OkType), Err(ErrType)
                        let (ok_type, err_type) = parse_result_base_type(base_type);
                        let mut variants = IndexMap::new();
                        variants.insert("Ok".to_string(), VariantDef {
                            name: "Ok".to_string(),
                            ty: Some(ok_type),
                            doc: Vec::new(),
                        });
                        variants.insert("Err".to_string(), VariantDef {
                            name: "Err".to_string(),
                            ty: Some(err_type),
                            doc: Vec::new(),
                        });
                        TypeDefKind::Enum {
                            variants,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string(), "Clone".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::CallbackWrapper => {
                        // impl_callback!(CallbackWrapper, OptionCallbackWrapper, CallbackValue, CallbackType)
                        // CallbackWrapper struct: cb (CallbackValue), data (RefAny)
                        let mut fields = IndexMap::new();
                        fields.insert("cb".to_string(), FieldDef {
                            name: "cb".to_string(),
                            ty: base_type.clone(), // CallbackValue
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        fields.insert("data".to_string(), FieldDef {
                            name: "data".to_string(),
                            ty: "RefAny".to_string(),
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        TypeDefKind::Struct {
                            fields,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string()],
                            custom_impls: vec![],
                        }
                    }
                    MacroGeneratedKind::CallbackValue => {
                        // CallbackValue struct: cb (CallbackType - the extern "C" fn)
                        let mut fields = IndexMap::new();
                        fields.insert("cb".to_string(), FieldDef {
                            name: "cb".to_string(),
                            ty: base_type.clone(), // CallbackType
                            ref_kind: RefKind::Value,
                            doc: Vec::new(),
                        });
                        TypeDefKind::Struct {
                            fields,
                            repr: Some("C".to_string()),
                            generic_params: vec![],
                            derives: vec!["Debug".to_string()],
                            custom_impls: vec![],
                        }
                    }
                }
            }
            // Not MacroGenerated - return as-is
            other => other.clone(),
        }
    }
    
    /// Get the expanded kind (expands MacroGenerated, returns original otherwise)
    pub fn get_expanded_kind(&self) -> TypeDefKind {
        self.expand_macro_generated()
    }
    
    /// Check if this is a MacroGenerated type
    pub fn is_macro_generated(&self) -> bool {
        matches!(&self.kind, TypeDefKind::MacroGenerated { .. })
    }
    
    /// Convert TypeDefinition to ParsedTypeInfo (for compatibility with existing code)
    /// This expands MacroGenerated types into their real Struct/Enum/CallbackTypedef form
    pub fn to_parsed_type_info(&self) -> crate::patch::index::ParsedTypeInfo {
        use crate::patch::index::{ParsedTypeInfo, TypeKind, FieldInfo, VariantInfo, CallbackArgInfo};
        use crate::api::BorrowMode;
        
        // Get the expanded kind (expands MacroGenerated to real types)
        let expanded_kind = self.expand_macro_generated();
        
        // Convert module_path from "mod1::mod2" to vec!["mod1", "mod2"]
        let module_path: Vec<String> = if self.module_path.is_empty() {
            vec![]
        } else {
            self.module_path.split("::").map(|s| s.to_string()).collect()
        };
        
        // Convert TypeDefKind to TypeKind
        let kind = match expanded_kind {
            TypeDefKind::Struct { fields, repr, generic_params, derives, custom_impls } => {
                TypeKind::Struct {
                    fields: fields.into_iter().map(|(name, field)| {
                        (name.clone(), FieldInfo {
                            name,
                            ty: field.ty,
                            ref_kind: field.ref_kind,
                            doc: if field.doc.is_empty() { None } else { Some(field.doc) },
                        })
                    }).collect(),
                    repr,
                    doc: None,
                    generic_params,
                    implemented_traits: custom_impls,
                    derives,
                }
            }
            TypeDefKind::Enum { variants, repr, generic_params, derives, custom_impls } => {
                TypeKind::Enum {
                    variants: variants.into_iter().map(|(name, variant)| {
                        (name.clone(), VariantInfo {
                            name,
                            ty: variant.ty,
                            doc: if variant.doc.is_empty() { None } else { Some(variant.doc) },
                        })
                    }).collect(),
                    repr,
                    doc: None,
                    generic_params,
                    implemented_traits: custom_impls,
                    derives,
                }
            }
            TypeDefKind::TypeAlias { target, generic_base, generic_args } => {
                TypeKind::TypeAlias {
                    target,
                    generic_base,
                    generic_args,
                    doc: None,
                }
            }
            TypeDefKind::CallbackTypedef { args, returns } => {
                TypeKind::CallbackTypedef {
                    fn_args: args.into_iter().map(|arg| {
                        // Keep RefKind as-is - no conversion needed anymore
                        // The api.json CallbackArgData now uses RefKind directly
                        CallbackArgInfo { ty: arg.ty, ref_kind: arg.ref_kind }
                    }).collect(),
                    returns,
                    doc: None,
                }
            }
            TypeDefKind::MacroGenerated { .. } => {
                // This shouldn't happen after expand_macro_generated(), but handle it
                TypeKind::Struct {
                    fields: IndexMap::new(),
                    repr: Some("C".to_string()),
                    doc: None,
                    generic_params: vec![],
                    implemented_traits: vec![],
                    derives: vec![],
                }
            }
        };
        
        ParsedTypeInfo {
            full_path: self.full_path.clone(),
            type_name: self.type_name.clone(),
            file_path: self.file_path.clone(),
            module_path,
            kind,
            source_code: self.source_code.clone(),
        }
    }
}

/// Parse "Result<OkType, ErrType>" into (OkType, ErrType)
fn parse_result_base_type(base_type: &str) -> (String, String) {
    // Handle "Result<OkType, ErrType>" format
    if base_type.starts_with("Result<") && base_type.ends_with('>') {
        let inner = &base_type[7..base_type.len()-1];
        // Split by comma, handling nested generics
        if let Some((ok, err)) = split_generic_args(inner) {
            return (ok.trim().to_string(), err.trim().to_string());
        }
    }
    // Fallback
    ("()".to_string(), "()".to_string())
}

/// Split "A, B" into ("A", "B"), handling nested generics
fn split_generic_args(s: &str) -> Option<(String, String)> {
    let mut depth = 0;
    for (i, c) in s.char_indices() {
        match c {
            '<' => depth += 1,
            '>' => depth -= 1,
            ',' if depth == 0 => {
                return Some((s[..i].to_string(), s[i+1..].to_string()));
            }
            _ => {}
        }
    }
    None
}

// ============================================================================
// TYPE INDEX
// ============================================================================

/// Fast lookup index for type definitions
#[derive(Debug, Default)]
pub struct TypeIndex {
    /// Map from simple type name to all definitions with that name
    by_name: HashMap<String, Vec<Arc<TypeDefinition>>>,
    /// Map from full path to definition
    by_path: HashMap<String, Arc<TypeDefinition>>,
    /// Errors encountered during indexing
    pub errors: Vec<String>,
}

impl TypeIndex {
    pub fn new() -> Self {
        Self::default()
    }

    /// Build the type index from a workspace root
    pub fn build(workspace_root: &Path, verbose: bool) -> Result<Self> {
        let mut index = Self::new();

        // Crate directories to scan
        let crate_dirs = [
            ("azul_core", "core/src"),
            ("azul_css", "css/src"),
            ("azul_layout", "layout/src"),
            ("azul_dll", "dll/src"),
        ];

        // Collect all .rs files
        let mut all_files: Vec<(String, PathBuf)> = Vec::new();

        for (crate_name, src_path) in &crate_dirs {
            let src_dir = workspace_root.join(src_path);
            if src_dir.exists() {
                collect_rust_files(&mut all_files, crate_name, &src_dir);
            }
        }

        if verbose {
            eprintln!("[TypeIndex] Found {} Rust files to parse", all_files.len());
        }

        // Parse files in parallel
        let results: Vec<_> = all_files
            .par_iter()
            .map(|(crate_name, file_path)| {
                parse_file_for_types(crate_name, file_path)
            })
            .collect();

        // Merge results
        for result in results {
            match result {
                Ok(types) => {
                    for typedef in types {
                        index.add_type(typedef);
                    }
                }
                Err(e) => {
                    index.errors.push(e);
                }
            }
        }

        if verbose {
            eprintln!(
                "[TypeIndex] Indexed {} unique type names, {} total paths",
                index.by_name.len(),
                index.by_path.len()
            );
        }

        Ok(index)
    }

    /// Add a type definition to the index
    fn add_type(&mut self, typedef: TypeDefinition) {
        let type_name = typedef.type_name.clone();
        let full_path = typedef.full_path.clone();
        let arc = Arc::new(typedef);

        self.by_name
            .entry(type_name)
            .or_default()
            .push(Arc::clone(&arc));
        self.by_path.insert(full_path, arc);
    }

    /// Find the best match for a type name, preferring certain crates
    pub fn resolve(&self, type_name: &str, preferred_crate: Option<&str>) -> Option<&TypeDefinition> {
        let candidates = self.by_name.get(type_name)?;

        if candidates.is_empty() {
            return None;
        }

        if candidates.len() == 1 {
            return Some(&candidates[0]);
        }

        // Score candidates
        let mut best: Option<&Arc<TypeDefinition>> = None;
        let mut best_score = i32::MIN;

        for candidate in candidates {
            let mut score = 0;

            // Prefer matching crate - this should take priority
            if let Some(pref) = preferred_crate {
                if candidate.crate_name == pref {
                    score += 200;
                }
            }

            // Prefer azul_core over others (but lower priority than explicit preference)
            // Blacklist azul_dll - it's being phased out, prefer azul_layout
            if candidate.crate_name == "azul_dll" {
                score -= 1000; // Strongly deprioritize azul_dll
            } else if candidate.crate_name == "azul_core" {
                score += 100;
            } else if candidate.crate_name == "azul_css" {
                score += 80;
            } else if candidate.crate_name == "azul_layout" {
                score += 60;
            }

            // Prefer non-macro-generated
            if !matches!(candidate.kind, TypeDefKind::MacroGenerated { .. }) {
                score += 10;
            }

            // Prefer types with repr(C) or other FFI-safe repr
            let has_ffi_repr = match &candidate.kind {
                TypeDefKind::Struct { repr, .. } |
                TypeDefKind::Enum { repr, .. } => is_ffi_safe_repr(repr),
                _ => false,
            };
            if has_ffi_repr {
                score += 20;
            }

            if score > best_score {
                best_score = score;
                best = Some(candidate);
            }
        }

        best.map(|arc| arc.as_ref())
    }

    /// Get a type by its full path
    pub fn get_by_path(&self, full_path: &str) -> Option<&TypeDefinition> {
        self.by_path.get(full_path).map(|arc| arc.as_ref())
    }

    /// Get all definitions for a type name
    pub fn get_all_by_name(&self, type_name: &str) -> Option<&Vec<Arc<TypeDefinition>>> {
        self.by_name.get(type_name)
    }

    /// Check if a type name is a primitive
    pub fn is_primitive(type_name: &str) -> bool {
        const PRIMITIVES: &[&str] = &[
            "i8", "i16", "i32", "i64", "i128", "isize",
            "u8", "u16", "u32", "u64", "u128", "usize",
            "f32", "f64", "bool", "char", "()", "c_void",
        ];
        
        let trimmed = type_name.trim();
        
        // Direct primitive check
        if PRIMITIVES.contains(&trimmed) {
            return true;
        }

        // Pointer types
        if trimmed.starts_with("*const ") || trimmed.starts_with("*mut ") {
            return true;
        }

        // References
        if trimmed.starts_with('&') {
            return true;
        }

        // Arrays
        if trimmed.starts_with('[') && trimmed.ends_with(']') {
            return true;
        }

        false
    }

    /// Get all type names in the index
    pub fn all_type_names(&self) -> impl Iterator<Item = &String> {
        self.by_name.keys()
    }
    
    /// Iterate over all types in the index, yielding (type_name, Vec<TypeDefinition>)
    pub fn iter_all(&self) -> impl Iterator<Item = (&String, &Vec<Arc<TypeDefinition>>)> {
        self.by_name.iter()
    }

    /// Get the number of unique type names
    pub fn type_count(&self) -> usize {
        self.by_name.len()
    }

    /// Add a type definition for testing purposes
    #[cfg(test)]
    pub fn add_type_for_test(&mut self, typedef: TypeDefinition) {
        let full_path = if typedef.module_path.is_empty() {
            format!("{}::{}", typedef.crate_name, typedef.type_name)
        } else {
            format!("{}::{}::{}", typedef.crate_name, typedef.module_path, typedef.type_name)
        };
        let arc = Arc::new(typedef.clone());
        self.by_name
            .entry(typedef.type_name.clone())
            .or_default()
            .push(Arc::clone(&arc));
        self.by_path.insert(full_path, arc);
    }
    
    // ========================================================================
    // COMPATIBILITY METHODS (for WorkspaceIndex replacement)
    // ========================================================================
    
    /// Find a type by name and return as ParsedTypeInfo (WorkspaceIndex compatibility)
    /// Also tries with "Az" prefix if not found
    pub fn find_type(&self, type_name: &str) -> Option<Vec<crate::patch::index::ParsedTypeInfo>> {
        // Try exact match first
        if let Some(defs) = self.by_name.get(type_name) {
            let parsed: Vec<_> = defs.iter().map(|d| d.to_parsed_type_info()).collect();
            if !parsed.is_empty() {
                return Some(parsed);
            }
        }
        // Try with "Az" prefix
        let az_prefixed = format!("Az{}", type_name);
        if let Some(defs) = self.by_name.get(&az_prefixed) {
            let parsed: Vec<_> = defs.iter().map(|d| d.to_parsed_type_info()).collect();
            if !parsed.is_empty() {
                return Some(parsed);
            }
        }
        None
    }
    
    /// Find a type by full path and return as ParsedTypeInfo (WorkspaceIndex compatibility)
    /// Also tries with "Az" prefix if not found
    pub fn find_type_by_path(&self, type_path: &str) -> Option<crate::patch::index::ParsedTypeInfo> {
        // Try exact match first
        if let Some(def) = self.by_path.get(type_path) {
            return Some(def.to_parsed_type_info());
        }
        // Try with "Az" prefix in path
        let az_prefixed_path = {
            let parts: Vec<&str> = type_path.rsplitn(2, "::").collect();
            if parts.len() == 2 {
                format!("{}::Az{}", parts[1], parts[0])
            } else {
                format!("Az{}", type_path)
            }
        };
        self.by_path.get(&az_prefixed_path).map(|d| d.to_parsed_type_info())
    }
    
    /// Resolve a type by name and return as ParsedTypeInfo
    pub fn resolve_as_parsed_type_info(&self, type_name: &str, preferred_crate: Option<&str>) -> Option<crate::patch::index::ParsedTypeInfo> {
        self.resolve(type_name, preferred_crate).map(|d| d.to_parsed_type_info())
    }
    
    /// Find type by string search (for compatibility with WorkspaceIndex)
    /// This uses the existing resolve mechanism but always expands MacroGenerated types
    pub fn find_type_by_string_search(&self, type_name: &str) -> Option<crate::patch::index::ParsedTypeInfo> {
        // First try exact name match
        if let Some(def) = self.resolve(type_name, None) {
            return Some(def.to_parsed_type_info());
        }
        // Try with "Az" prefix
        let az_prefixed = format!("Az{}", type_name);
        if let Some(def) = self.resolve(&az_prefixed, None) {
            return Some(def.to_parsed_type_info());
        }
        None
    }
}

// ============================================================================
// FILE COLLECTION
// ============================================================================

/// Recursively collect all .rs files in a directory
fn collect_rust_files(files: &mut Vec<(String, PathBuf)>, crate_name: &str, dir: &Path) {
    let entries = match fs::read_dir(dir) {
        Ok(e) => e,
        Err(_) => return,
    };

    for entry in entries.flatten() {
        let path = entry.path();
        
        // Skip excluded paths (tests, examples, build.rs, etc.)
        if crate::autofix::module_map::should_exclude_path(&path) {
            continue;
        }

        if path.is_dir() {
            collect_rust_files(files, crate_name, &path);
        } else if path.extension().map_or(false, |e| e == "rs") {
            files.push((crate_name.to_string(), path));
        }
    }
}

// ============================================================================
// FILE PARSING
// ============================================================================

/// Parse a single file and extract type definitions
fn parse_file_for_types(crate_name: &str, file_path: &Path) -> Result<Vec<TypeDefinition>, String> {
    let content = fs::read_to_string(file_path)
        .map_err(|e| format!("Failed to read {}: {}", file_path.display(), e))?;

    let syntax_tree: File = syn::parse_file(&content)
        .map_err(|e| format!("Failed to parse {}: {}", file_path.display(), e))?;

    let module_path = infer_module_path(crate_name, file_path);
    let mut types = Vec::new();
    
    // First pass: collect all type definitions
    for item in &syntax_tree.items {
        match item {
            // SKIP Item::Use entirely - re-exports are NOT definitions
            Item::Use(_) => continue,

            Item::Struct(s) => {
                if let Some(typedef) = extract_struct(crate_name, &module_path, file_path, s) {
                    types.push(typedef);
                }
            }

            Item::Enum(e) => {
                if let Some(typedef) = extract_enum(crate_name, &module_path, file_path, e) {
                    types.push(typedef);
                }
            }

            Item::Type(t) => {
                if let Some(typedef) = extract_type_alias(crate_name, &module_path, file_path, t) {
                    types.push(typedef);
                }
            }

            Item::Macro(m) => {
                // Handle impl_vec!, impl_option!, etc.
                let generated = extract_macro_generated_types(crate_name, &module_path, file_path, m);
                types.extend(generated);
            }

            Item::Mod(m) => {
                // Recursively extract types from inline modules (e.g., `mod decode { ... }`)
                if let Some((_, items)) = &m.content {
                    let mod_name = m.ident.to_string();
                    let nested_module_path = if module_path.is_empty() {
                        mod_name.clone()
                    } else {
                        format!("{}::{}", module_path, mod_name)
                    };
                    let nested_types = extract_types_from_items(crate_name, &nested_module_path, file_path, items);
                    types.extend(nested_types);
                }
            }

            _ => {}
        }
    }

    // Second pass: extract manual trait implementations (`impl Trait for Type`)
    // and attach them to the corresponding type definitions
    let custom_impls_map = extract_custom_impls_from_items(&syntax_tree.items);
    
    // Attach custom_impls to types
    for typedef in &mut types {
        if let Some(impls) = custom_impls_map.get(&typedef.type_name) {
            attach_custom_impls(typedef, impls.clone());
        }
    }

    Ok(types)
}

/// Extract types from a list of items (used for both top-level and nested modules)
fn extract_types_from_items(crate_name: &str, module_path: &str, file_path: &Path, items: &[Item]) -> Vec<TypeDefinition> {
    let mut types = Vec::new();
    
    // First pass: collect type definitions
    for item in items {
        match item {
            Item::Use(_) => continue,
            
            Item::Struct(s) => {
                if let Some(typedef) = extract_struct(crate_name, module_path, file_path, s) {
                    types.push(typedef);
                }
            }
            
            Item::Enum(e) => {
                if let Some(typedef) = extract_enum(crate_name, module_path, file_path, e) {
                    types.push(typedef);
                }
            }
            
            Item::Type(t) => {
                if let Some(typedef) = extract_type_alias(crate_name, module_path, file_path, t) {
                    types.push(typedef);
                }
            }
            
            Item::Macro(m) => {
                let generated = extract_macro_generated_types(crate_name, module_path, file_path, m);
                types.extend(generated);
            }
            
            Item::Mod(m) => {
                // Recursively handle nested modules
                if let Some((_, nested_items)) = &m.content {
                    let mod_name = m.ident.to_string();
                    let nested_module_path = if module_path.is_empty() {
                        mod_name.clone()
                    } else {
                        format!("{}::{}", module_path, mod_name)
                    };
                    let nested_types = extract_types_from_items(crate_name, &nested_module_path, file_path, nested_items);
                    types.extend(nested_types);
                }
            }
            
            _ => {}
        }
    }
    
    // Second pass: extract custom trait implementations and attach to types
    let custom_impls_map = extract_custom_impls_from_items(items);
    for typedef in &mut types {
        if let Some(impls) = custom_impls_map.get(&typedef.type_name) {
            attach_custom_impls(typedef, impls.clone());
        }
    }
    
    types
}

/// Infer the module path from file path
fn infer_module_path(crate_name: &str, file_path: &Path) -> String {
    // Extract path components after "src/"
    let path_str = file_path.to_string_lossy();
    
    let after_src = if let Some(idx) = path_str.find("/src/") {
        &path_str[idx + 5..]
    } else {
        return String::new();
    };

    // Remove .rs extension and handle mod.rs/lib.rs
    let without_ext = after_src.trim_end_matches(".rs");
    
    if without_ext == "lib" || without_ext == "mod" || without_ext.ends_with("/mod") || without_ext.ends_with("/lib") {
        // lib.rs or mod.rs - module path is the parent directory
        let parent = without_ext.rsplit_once('/').map(|(p, _)| p).unwrap_or("");
        parent.replace('/', "::")
    } else {
        without_ext.replace('/', "::")
    }
}

/// Build full path from crate name, module path, and type name
fn build_full_path(crate_name: &str, module_path: &str, type_name: &str) -> String {
    if module_path.is_empty() {
        format!("{}::{}", crate_name, type_name)
    } else {
        format!("{}::{}::{}", crate_name, module_path, type_name)
    }
}

// ============================================================================
// TYPE EXTRACTION
// ============================================================================

/// Strip the "Az" prefix from a type name if present.
/// For example, `AzInstantPtr` -> `InstantPtr`, `String` -> `String`
/// This is used because source code uses "Az" prefixes for disambiguation,
/// but api.json should store types WITHOUT the prefix (prefix is added later by codegen).
fn strip_az_prefix(type_name: &str) -> String {
    // Don't strip "Az" if the remaining name would be too short or empty
    if type_name.starts_with("Az") && type_name.len() > 2 {
        // Check that the character after "Az" is uppercase (to avoid stripping "Azure" etc.)
        let after_az = &type_name[2..];
        if after_az.chars().next().map(|c| c.is_ascii_uppercase()).unwrap_or(false) {
            return after_az.to_string();
        }
    }
    type_name.to_string()
}

/// Extract just the type name from a syn::Type, without the full path.
/// For example, `crate::props::basic::color::ColorU` -> `ColorU`
/// For generic types like `Option<T>`, returns `Option<T>`.
/// NOTE: This also strips the "Az" prefix from type names.
fn extract_type_name(ty: &syn::Type) -> String {
    match ty {
        syn::Type::Path(type_path) => {
            // Get the last segment of the path (the actual type name)
            if let Some(segment) = type_path.path.segments.last() {
                let ident = strip_az_prefix(&segment.ident.to_string());
                // Check if there are generic arguments
                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
                    // Recursively extract type names for generic arguments
                    let generic_args: Vec<String> = args.args.iter().filter_map(|arg| {
                        match arg {
                            syn::GenericArgument::Type(inner_ty) => Some(extract_type_name(inner_ty)),
                            _ => None,
                        }
                    }).collect();
                    if generic_args.is_empty() {
                        ident
                    } else {
                        format!("{}<{}>", ident, generic_args.join(", "))
                    }
                } else {
                    ident
                }
            } else {
                // Fallback: no segments, use token stream
                clean_type_string(&ty.to_token_stream().to_string())
            }
        }
        syn::Type::Reference(ref_type) => {
            // &T or &mut T
            let inner = extract_type_name(&ref_type.elem);
            if ref_type.mutability.is_some() {
                format!("&mut {}", inner)
            } else {
                format!("&{}", inner)
            }
        }
        syn::Type::Ptr(ptr_type) => {
            // *const T or *mut T
            let inner = extract_type_name(&ptr_type.elem);
            if ptr_type.mutability.is_some() {
                format!("*mut {}", inner)
            } else {
                format!("*const {}", inner)
            }
        }
        syn::Type::Slice(slice_type) => {
            format!("[{}]", extract_type_name(&slice_type.elem))
        }
        syn::Type::Array(array_type) => {
            let elem = extract_type_name(&array_type.elem);
            let len = array_type.len.to_token_stream().to_string();
            format!("[{}; {}]", elem, len)
        }
        syn::Type::Tuple(tuple_type) => {
            let elems: Vec<String> = tuple_type.elems.iter().map(extract_type_name).collect();
            format!("({})", elems.join(", "))
        }
        _ => {
            // Fallback for other types
            clean_type_string(&ty.to_token_stream().to_string())
        }
    }
}

/// Extract field type and ref_kind for struct fields.
/// For pointer types (*const T, *mut T), Box<T>, and Option<Box<T>>, the type is replaced with "c_void"
/// and the ref_kind is set appropriately. This prevents recursion through opaque pointers.
fn extract_struct_field_type(ty: &syn::Type) -> (String, RefKind) {
    match ty {
        syn::Type::Ptr(ptr_type) => {
            // *const T or *mut T -> type becomes "c_void", ref_kind is ConstPtr/MutPtr
            if ptr_type.mutability.is_some() {
                ("c_void".to_string(), RefKind::MutPtr)
            } else {
                ("c_void".to_string(), RefKind::ConstPtr)
            }
        }
        syn::Type::Path(type_path) => {
            if let Some(segment) = type_path.path.segments.last() {
                // Check if it's Box<T>
                if segment.ident == "Box" {
                    // Box<T> -> type becomes "c_void", ref_kind is Boxed
                    return ("c_void".to_string(), RefKind::Boxed);
                }
                // Check if it's Option<Box<T>>
                if segment.ident == "Option" {
                    if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
                        if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
                            // Check if the inner type is Box<T>
                            if let syn::Type::Path(inner_path) = inner_ty {
                                if let Some(inner_segment) = inner_path.path.segments.last() {
                                    if inner_segment.ident == "Box" {
                                        // Option<Box<T>> -> type becomes "c_void", ref_kind is OptionBoxed
                                        return ("c_void".to_string(), RefKind::OptionBoxed);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // Regular type - extract just the type name (not the full path)
            (extract_type_name(ty), RefKind::Value)
        }
        _ => {
            // Any other type - extract just the type name
            (extract_type_name(ty), RefKind::Value)
        }
    }
}

/// Extract a struct definition
fn extract_struct(
    crate_name: &str,
    module_path: &str,
    file_path: &Path,
    s: &syn::ItemStruct,
) -> Option<TypeDefinition> {
    let type_name = s.ident.to_string();
    let full_path = build_full_path(crate_name, module_path, &type_name);

    let generic_params: Vec<String> = s
        .generics
        .type_params()
        .map(|tp| tp.ident.to_string())
        .collect();

    let mut fields = IndexMap::new();
    for field in s.fields.iter() {
        if let Some(field_name) = field.ident.as_ref() {
            let (field_ty, ref_kind) = extract_struct_field_type(&field.ty);
            fields.insert(
                field_name.to_string(),
                FieldDef {
                    name: field_name.to_string(),
                    ty: field_ty,
                    ref_kind,
                    doc: extract_doc_comments(&field.attrs),
                },
            );
        }
    }

    let repr = extract_repr_attr(&s.attrs);
    let derives = extract_derives(&s.attrs);

    Some(TypeDefinition {
        full_path,
        type_name,
        file_path: file_path.to_path_buf(),
        module_path: module_path.to_string(),
        crate_name: crate_name.to_string(),
        kind: TypeDefKind::Struct {
            fields,
            repr,
            generic_params,
            derives,
            custom_impls: vec![], // Will be filled in by second pass
        },
        source_code: s.to_token_stream().to_string(),
    })
}

/// Extract an enum definition
fn extract_enum(
    crate_name: &str,
    module_path: &str,
    file_path: &Path,
    e: &syn::ItemEnum,
) -> Option<TypeDefinition> {
    let type_name = e.ident.to_string();
    let full_path = build_full_path(crate_name, module_path, &type_name);

    let generic_params: Vec<String> = e
        .generics
        .type_params()
        .map(|tp| tp.ident.to_string())
        .collect();

    let mut variants = IndexMap::new();
    for variant in &e.variants {
        let variant_name = variant.ident.to_string();
        let variant_ty = if variant.fields.is_empty() {
            None
        } else {
            let fields_str = variant
                .fields
                .iter()
                .map(|f| extract_type_name(&f.ty))
                .collect::<Vec<_>>()
                .join(", ");
            Some(fields_str)
        };

        variants.insert(
            variant_name.clone(),
            VariantDef {
                name: variant_name,
                ty: variant_ty,
                doc: extract_doc_comments(&variant.attrs),
            },
        );
    }

    let repr = extract_repr_attr(&e.attrs);
    let derives = extract_derives(&e.attrs);

    Some(TypeDefinition {
        full_path,
        type_name,
        file_path: file_path.to_path_buf(),
        module_path: module_path.to_string(),
        crate_name: crate_name.to_string(),
        kind: TypeDefKind::Enum {
            variants,
            repr,
            generic_params,
            derives,
            custom_impls: vec![], // Will be filled in by second pass
        },
        source_code: e.to_token_stream().to_string(),
    })
}

/// Extract reference kind and base type from a syn::Type
/// Returns (base_type_string, RefKind)
fn extract_ref_kind_from_syn_type(ty: &syn::Type) -> (String, RefKind) {
    match ty {
        syn::Type::Reference(ref_type) => {
            // &T or &mut T
            let inner_type = extract_type_name(&ref_type.elem);
            if ref_type.mutability.is_some() {
                (inner_type, RefKind::RefMut)
            } else {
                (inner_type, RefKind::Ref)
            }
        }
        syn::Type::Ptr(ptr_type) => {
            // *const T or *mut T - extract base type and use ConstPtr/MutPtr
            let inner_type = extract_type_name(&ptr_type.elem);
            if ptr_type.mutability.is_some() {
                (inner_type, RefKind::MutPtr)
            } else {
                (inner_type, RefKind::ConstPtr)
            }
        }
        _ => {
            // Any other type is passed by value
            (extract_type_name(ty), RefKind::Value)
        }
    }
}

/// Extract a type alias
fn extract_type_alias(
    crate_name: &str,
    module_path: &str,
    file_path: &Path,
    t: &syn::ItemType,
) -> Option<TypeDefinition> {
    let type_name = t.ident.to_string();
    let full_path = build_full_path(crate_name, module_path, &type_name);

    // Check if this is a callback typedef (extern "C" fn)
    if let syn::Type::BareFn(bare_fn) = &*t.ty {
        let args: Vec<CallbackArg> = bare_fn
            .inputs
            .iter()
            .map(|arg| {
                let (base_type, ref_kind) = extract_ref_kind_from_syn_type(&arg.ty);
                CallbackArg {
                    name: arg.name.as_ref().map(|(ident, _)| ident.to_string()),
                    ty: base_type,
                    ref_kind,
                }
            })
            .collect();

        let returns = match &bare_fn.output {
            syn::ReturnType::Default => None,
            syn::ReturnType::Type(_, ty) => {
                let ret_str = extract_type_name(ty);
                if ret_str.is_empty() || ret_str == "()" {
                    None
                } else {
                    Some(ret_str)
                }
            }
        };

        return Some(TypeDefinition {
            full_path,
            type_name,
            file_path: file_path.to_path_buf(),
            module_path: module_path.to_string(),
            crate_name: crate_name.to_string(),
            kind: TypeDefKind::CallbackTypedef { args, returns },
            source_code: t.to_token_stream().to_string(),
        });
    }

    // Try to extract generic arguments from the type
    let (generic_base, generic_args) = extract_generic_args_from_type(&t.ty);
    
    // Extract just the type name for the target, not the full path
    let target = extract_type_name(&t.ty);
    
    Some(TypeDefinition {
        full_path,
        type_name,
        file_path: file_path.to_path_buf(),
        module_path: module_path.to_string(),
        crate_name: crate_name.to_string(),
        kind: TypeDefKind::TypeAlias {
            target,
            generic_base,
            generic_args,
        },
        source_code: t.to_token_stream().to_string(),
    })
}

/// Extract generic base type and arguments from a syn::Type
/// Returns (Some(base_type), [args]) for generic types, (None, []) for non-generic types
fn extract_generic_args_from_type(ty: &syn::Type) -> (Option<String>, Vec<String>) {
    match ty {
        syn::Type::Path(type_path) => {
            // Get the last segment (the actual type name)
            if let Some(segment) = type_path.path.segments.last() {
                let base_type = segment.ident.to_string();
                
                // Check for generic arguments
                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
                    let generic_args: Vec<String> = args.args.iter()
                        .filter_map(|arg| {
                            match arg {
                                syn::GenericArgument::Type(ty) => {
                                    // Recursively get the type name
                                    Some(extract_type_name(ty))
                                }
                                syn::GenericArgument::Lifetime(_) => None,
                                _ => None,
                            }
                        })
                        .collect();
                    
                    if !generic_args.is_empty() {
                        return (Some(base_type), generic_args);
                    }
                }
            }
            (None, vec![])
        }
        _ => (None, vec![]),
    }
}

/// Extract types generated by macros like impl_vec!, impl_option!
fn extract_macro_generated_types(
    crate_name: &str,
    module_path: &str,
    file_path: &Path,
    m: &syn::ItemMacro,
) -> Vec<TypeDefinition> {
    let mut types = Vec::new();

    // Get macro name
    let macro_name = m.mac.path.segments
        .last()
        .map(|s| s.ident.to_string())
        .unwrap_or_default();

    let tokens = m.mac.tokens.to_string();
    let args: Vec<&str> = tokens.split(',').map(|s| s.trim()).collect();

    match macro_name.as_str() {
        "impl_vec" => {
            // impl_vec!(BaseType, VecType, DestructorType)
            // Generates:
            //   - VecType (struct with ptr, len, cap, destructor fields)
            //   - DestructorType (enum with DefaultRust, NoDestructor, External variants)
            //   - DestructorTypeType (callback_typedef for extern "C" fn(*mut VecType))
            // NOTE: impl_vec_clone and impl_vec_debug only add trait impls, not new types
            if args.len() >= 3 {
                let base_type = args[0].to_string();
                let vec_type = args[1].to_string();
                let destructor_type = args[2].to_string();
                let destructor_type_type = format!("{}Type", destructor_type);

                // VecType (struct)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &vec_type),
                    type_name: vec_type.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name.clone(),
                        base_type: base_type.clone(),
                        kind: MacroGeneratedKind::Vec,
                    },
                    source_code: m.to_token_stream().to_string(),
                });

                // DestructorType (enum)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &destructor_type),
                    type_name: destructor_type.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name.clone(),
                        base_type: base_type.clone(),
                        kind: MacroGeneratedKind::VecDestructor,
                    },
                    source_code: m.to_token_stream().to_string(),
                });

                // DestructorTypeType (callback_typedef for the External variant's fn pointer)
                // This is: extern "C" fn(*mut VecType)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &destructor_type_type),
                    type_name: destructor_type_type,
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name,
                        base_type: vec_type, // The vec type is referenced in the callback
                        kind: MacroGeneratedKind::VecDestructorType,
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        "impl_option" => {
            // impl_option! can have different forms:
            // impl_option!(BaseType, OptionType, EnumWrapperType, [tag_type])  - 3+ args
            // impl_option!(BaseType, OptionType, copy = false, [...])          - 2 args (no wrapper)
            // impl_option!(BaseType, OptionType, [...])                        - 2 args (no wrapper)
            if args.len() >= 2 {
                let base_type = args[0].to_string();
                let option_type = args[1].to_string();
                
                // Check if 3rd arg looks like a type name (not "copy" or "[")
                let has_wrapper = args.len() >= 3 && {
                    let third = args[2].trim();
                    !third.starts_with("copy") && 
                    !third.starts_with("clone") && 
                    !third.starts_with("[") &&
                    !third.is_empty()
                };

                // OptionType (always generated)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &option_type),
                    type_name: option_type.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name.clone(),
                        base_type: base_type.clone(),
                        kind: MacroGeneratedKind::Option,
                    },
                    source_code: m.to_token_stream().to_string(),
                });

                // EnumWrapperType (only if 3rd arg is a type name)
                if has_wrapper {
                    let wrapper_type = args[2].to_string();
                    types.push(TypeDefinition {
                        full_path: build_full_path(crate_name, module_path, &wrapper_type),
                        type_name: wrapper_type,
                        file_path: file_path.to_path_buf(),
                        module_path: module_path.to_string(),
                        crate_name: crate_name.to_string(),
                        kind: TypeDefKind::MacroGenerated {
                            source_macro: macro_name,
                            base_type,
                            kind: MacroGeneratedKind::OptionEnumWrapper,
                        },
                        source_code: m.to_token_stream().to_string(),
                    });
                }
            }
        }

        "impl_result" => {
            // impl_result!(OkType, ErrType, ResultType, copy = false, [...])
            // Generates: ResultType (enum with Ok/Err variants)
            if args.len() >= 3 {
                let ok_type = args[0].to_string();
                let err_type = args[1].to_string();
                let result_type = args[2].to_string();

                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &result_type),
                    type_name: result_type,
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name,
                        base_type: format!("Result<{}, {}>", ok_type, err_type),
                        kind: MacroGeneratedKind::Result,
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        "impl_callback" => {
            // impl_callback!(CallbackWrapper, OptionCallbackWrapper, CallbackValue, CallbackType)
            // Generates: CallbackWrapper (struct), OptionCallbackWrapper, CallbackValue (struct)
            // CallbackType is already a type alias defined separately
            if args.len() >= 4 {
                let callback_wrapper = args[0].to_string();
                let option_callback_wrapper = args[1].to_string();
                let callback_value = args[2].to_string();
                let callback_type = args[3].to_string(); // The extern "C" fn type

                // CallbackWrapper struct - has fields: data (RefAny), callback (CallbackValue)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &callback_wrapper),
                    type_name: callback_wrapper.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name.clone(),
                        base_type: callback_value.clone(), // References CallbackValue
                        kind: MacroGeneratedKind::CallbackWrapper,
                    },
                    source_code: m.to_token_stream().to_string(),
                });

                // OptionCallbackWrapper (via nested impl_option!)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &option_callback_wrapper),
                    type_name: option_callback_wrapper,
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name.clone(),
                        base_type: callback_wrapper.clone(),
                        kind: MacroGeneratedKind::Option,
                    },
                    source_code: m.to_token_stream().to_string(),
                });

                // CallbackValue struct - has field: cb (CallbackType)
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &callback_value),
                    type_name: callback_value.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::MacroGenerated {
                        source_macro: macro_name,
                        base_type: callback_type, // References CallbackType (the extern "C" fn)
                        kind: MacroGeneratedKind::CallbackValue,
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        // ====================================================================
        // CSS PROPERTY MACROS - Generate wrapper structs around PixelValue/etc.
        // ====================================================================
        
        "define_dimension_property" => {
            // define_dimension_property!(LayoutMaxWidth, || Self { inner: PixelValue::px(core::f32::MAX) });
            // Generates a struct with #[repr(C)] and inner: PixelValue
            if !args.is_empty() {
                let struct_name = args[0].to_string();
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &struct_name),
                    type_name: struct_name.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::Struct {
                        fields: {
                            let mut fields = IndexMap::new();
                            fields.insert("inner".to_string(), FieldDef {
                                name: "inner".to_string(),
                                ty: "PixelValue".to_string(),
                                ref_kind: RefKind::Value,
                                doc: Vec::new(),
                            });
                            fields
                        },
                        repr: Some("C".to_string()),
                        generic_params: vec![],
                        derives: vec![
                            "Debug".to_string(), "Copy".to_string(), "Clone".to_string(),
                            "PartialEq".to_string(), "Eq".to_string(), "PartialOrd".to_string(),
                            "Ord".to_string(), "Hash".to_string(),
                        ],
                        custom_impls: vec![],
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        "define_position_property" => {
            // define_position_property!(LayoutTop);
            // Generates a struct with #[repr(C)] and inner: PixelValue
            if !args.is_empty() {
                let struct_name = args[0].to_string();
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &struct_name),
                    type_name: struct_name.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::Struct {
                        fields: {
                            let mut fields = IndexMap::new();
                            fields.insert("inner".to_string(), FieldDef {
                                name: "inner".to_string(),
                                ty: "PixelValue".to_string(),
                                ref_kind: RefKind::Value,
                                doc: Vec::new(),
                            });
                            fields
                        },
                        repr: Some("C".to_string()),
                        generic_params: vec![],
                        derives: vec![
                            "Default".to_string(), "Copy".to_string(), "Clone".to_string(),
                            "PartialEq".to_string(), "Eq".to_string(), "PartialOrd".to_string(),
                            "Ord".to_string(), "Hash".to_string(),
                        ],
                        custom_impls: vec![],
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        "define_border_side_property" => {
            // define_border_side_property!(StyleBorderTopStyle, BorderStyle, BorderStyle::None);
            // define_border_side_property!(StyleBorderTopColor, ColorU);
            // define_border_side_property!(LayoutBorderTopWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
            if !args.is_empty() {
                let struct_name = args[0].to_string();
                let inner_type = if args.len() > 1 { 
                    args[1].to_string() 
                } else { 
                    "PixelValue".to_string() 
                };
                
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &struct_name),
                    type_name: struct_name.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::Struct {
                        fields: {
                            let mut fields = IndexMap::new();
                            fields.insert("inner".to_string(), FieldDef {
                                name: "inner".to_string(),
                                ty: inner_type,
                                ref_kind: RefKind::Value,
                                doc: Vec::new(),
                            });
                            fields
                        },
                        repr: Some("C".to_string()),
                        generic_params: vec![],
                        derives: vec![
                            "Copy".to_string(), "Clone".to_string(),
                            "PartialEq".to_string(), "Eq".to_string(), "PartialOrd".to_string(),
                            "Ord".to_string(), "Hash".to_string(),
                        ],
                        custom_impls: vec![],
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        "define_border_radius_property" => {
            // define_border_radius_property!(StyleBorderTopLeftRadius);
            // Generates a struct with inner: PixelValue
            if !args.is_empty() {
                let struct_name = args[0].to_string();
                types.push(TypeDefinition {
                    full_path: build_full_path(crate_name, module_path, &struct_name),
                    type_name: struct_name.clone(),
                    file_path: file_path.to_path_buf(),
                    module_path: module_path.to_string(),
                    crate_name: crate_name.to_string(),
                    kind: TypeDefKind::Struct {
                        fields: {
                            let mut fields = IndexMap::new();
                            fields.insert("inner".to_string(), FieldDef {
                                name: "inner".to_string(),
                                ty: "PixelValue".to_string(),
                                ref_kind: RefKind::Value,
                                doc: Vec::new(),
                            });
                            fields
                        },
                        repr: Some("C".to_string()),
                        generic_params: vec![],
                        derives: vec![
                            "Default".to_string(), "Copy".to_string(), "Clone".to_string(),
                            "PartialEq".to_string(), "Eq".to_string(), "PartialOrd".to_string(),
                            "Ord".to_string(), "Hash".to_string(),
                        ],
                        custom_impls: vec![],
                    },
                    source_code: m.to_token_stream().to_string(),
                });
            }
        }

        _ => {}
    }

    types
}

// ============================================================================
// HELPERS
// ============================================================================

/// Extract the #[repr(...)] attribute value if present.
/// Returns Some("C"), Some("C, u8"), Some("transparent"), etc. or None if no repr attribute.
fn extract_repr_attr(attrs: &[syn::Attribute]) -> Option<String> {
    for attr in attrs {
        if !attr.path().is_ident("repr") {
            continue;
        }
        // Parse the repr content: #[repr(C)] -> "C", #[repr(C, u8)] -> "C, u8"
        if let syn::Meta::List(list) = &attr.meta {
            let tokens = list.tokens.to_string();
            // Clean up whitespace
            let repr_value = tokens.split(',')
                .map(|s| s.trim())
                .collect::<Vec<_>>()
                .join(", ");
            if !repr_value.is_empty() {
                return Some(repr_value);
            }
        }
    }
    None
}

/// Check if a repr value indicates FFI-safe representation
fn is_ffi_safe_repr(repr: &Option<String>) -> bool {
    match repr {
        Some(r) => {
            r.contains("C")
                || r.contains("transparent")
                || r == "u8" || r == "u16" || r == "u32" || r == "u64"
                || r == "i8" || r == "i16" || r == "i32" || r == "i64"
                || r == "usize" || r == "isize"
        }
        None => false,
    }
}

/// Extract #[derive(...)] traits
fn extract_derives(attrs: &[syn::Attribute]) -> Vec<String> {
    let mut derives = Vec::new();
    for attr in attrs {
        if attr.path().is_ident("derive") {
            if let syn::Meta::List(list) = &attr.meta {
                let tokens = list.tokens.to_string();
                for item in tokens.split(',') {
                    let item = item.trim();
                    if !item.is_empty() {
                        derives.push(item.to_string());
                    }
                }
            }
        }
    }
    derives
}

/// Extract custom trait implementations from `impl Trait for Type` blocks.
/// Returns a map from type name to list of implemented traits.
fn extract_custom_impls_from_items(items: &[Item]) -> HashMap<String, Vec<String>> {
    let mut custom_impls: HashMap<String, Vec<String>> = HashMap::new();
    
    for item in items {
        if let Item::Impl(impl_item) = item {
            // Only interested in `impl Trait for Type`, not inherent impls
            if let Some((_, trait_path, _)) = &impl_item.trait_ {
                // Get the trait name (last segment of path)
                let trait_name = trait_path.segments.last()
                    .map(|seg| seg.ident.to_string())
                    .unwrap_or_default();
                
                // Get the type name that this trait is implemented for
                if let syn::Type::Path(type_path) = impl_item.self_ty.as_ref() {
                    let type_name = type_path.path.segments.last()
                        .map(|seg| seg.ident.to_string())
                        .unwrap_or_default();
                    
                    // We're interested in standard traits that affect code generation
                    let relevant_traits = [
                        "Clone", "Copy", "Debug", "Default", "Drop",
                        "PartialEq", "Eq", "PartialOrd", "Ord", "Hash",
                        "Display", "From", "Into", "AsRef", "AsMut",
                        "Deref", "DerefMut", "Iterator", "IntoIterator",
                    ];
                    
                    if relevant_traits.contains(&trait_name.as_str()) && !type_name.is_empty() {
                        custom_impls
                            .entry(type_name)
                            .or_default()
                            .push(trait_name);
                    }
                }
            }
        }
        
        // Recursively handle inline modules
        if let Item::Mod(m) = item {
            if let Some((_, nested_items)) = &m.content {
                let nested_impls = extract_custom_impls_from_items(nested_items);
                for (type_name, traits) in nested_impls {
                    custom_impls
                        .entry(type_name)
                        .or_default()
                        .extend(traits);
                }
            }
        }
    }
    
    custom_impls
}

/// Attach custom_impls to a TypeDefinition
fn attach_custom_impls(typedef: &mut TypeDefinition, impls: Vec<String>) {
    match &mut typedef.kind {
        TypeDefKind::Struct { custom_impls, .. } => {
            custom_impls.extend(impls);
            custom_impls.sort();
            custom_impls.dedup();
        }
        TypeDefKind::Enum { custom_impls, .. } => {
            custom_impls.extend(impls);
            custom_impls.sort();
            custom_impls.dedup();
        }
        // Other kinds don't have custom_impls
        _ => {}
    }
}

/// Extract doc comments from attributes as a multi-line vector
fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
    let mut docs = Vec::new();
    for attr in attrs {
        if attr.path().is_ident("doc") {
            if let syn::Meta::NameValue(nv) = &attr.meta {
                if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }) = &nv.value {
                    let line = s.value().trim().to_string();
                    if !line.is_empty() {
                        docs.push(line);
                    }
                }
            }
        }
    }
    docs
}

/// Clean up a type string (remove extra whitespace, normalize)
fn clean_type_string(s: &str) -> String {
    let result = s.split_whitespace().collect::<Vec<_>>().join(" ");
    // Fix syn's spacing for raw pointers: "* const T" -> "*const T", "* mut T" -> "*mut T"
    result
        .replace("* const", "*const")
        .replace("* mut", "*mut")
        // Also fix reference spacing: "& mut T" -> "&mut T", though extract_ref_kind_from_syn_type
        // handles these directly, this is for safety
        .replace("& mut", "&mut")
        .replace("& 'a", "&'a")
}

// ============================================================================
// UNIT TESTS
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    fn extract_types_from_source(source: &str) -> Vec<TypeDefinition> {
        let syntax_tree: File = syn::parse_file(source).expect("Failed to parse");
        let mut types = Vec::new();

        for item in &syntax_tree.items {
            match item {
                Item::Use(_) => continue, // Skip use statements
                Item::Struct(s) => {
                    if let Some(t) = extract_struct("test_crate", "", Path::new("test.rs"), s) {
                        types.push(t);
                    }
                }
                Item::Enum(e) => {
                    if let Some(t) = extract_enum("test_crate", "", Path::new("test.rs"), e) {
                        types.push(t);
                    }
                }
                Item::Type(t) => {
                    if let Some(td) = extract_type_alias("test_crate", "", Path::new("test.rs"), t) {
                        types.push(td);
                    }
                }
                Item::Macro(m) => {
                    let generated = extract_macro_generated_types("test_crate", "", Path::new("test.rs"), m);
                    types.extend(generated);
                }
                _ => {}
            }
        }

        types
    }

    #[test]
    fn test_skips_use_imports() {
        let source = r#"
            use azul_core::resources::FontCache;
            pub use other::Thing;
        "#;
        let types = extract_types_from_source(source);
        assert!(types.is_empty(), "use statements should not create type definitions");
    }

    #[test]
    fn test_extracts_struct_definition() {
        let source = r#"
            #[repr(C)]
            pub struct FontCache {
                pub fonts: FontVec,
            }
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        assert_eq!(types[0].type_name, "FontCache");
        assert_eq!(types[0].full_path, "test_crate::FontCache");
        
        match &types[0].kind {
            TypeDefKind::Struct { repr, fields, .. } => {
                assert!(repr.is_some());
                assert!(fields.contains_key("fonts"));
            }
            _ => panic!("Expected Struct"),
        }
    }

    #[test]
    fn test_extracts_enum_definition() {
        let source = r#"
            #[repr(C)]
            pub enum Color {
                Red,
                Green,
                Blue,
            }
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        assert_eq!(types[0].type_name, "Color");
        
        match &types[0].kind {
            TypeDefKind::Enum { variants, repr, .. } => {
                assert!(repr.is_some());
                assert_eq!(variants.len(), 3);
                assert!(variants.contains_key("Red"));
            }
            _ => panic!("Expected Enum"),
        }
    }

    #[test]
    fn test_extracts_type_alias() {
        let source = r#"
            pub type MyCallback = extern "C" fn(data: *const u8) -> bool;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        assert_eq!(types[0].type_name, "MyCallback");
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                assert_eq!(returns.as_deref(), Some("bool"));
            }
            _ => panic!("Expected CallbackTypedef"),
        }
    }

    #[test]
    fn test_callback_typedef_with_ref_mut() {
        // Test that &mut T is correctly parsed with RefKind::RefMut and base type T
        let source = r#"
            pub type OnClickCallback = extern "C" fn(data: &mut RefAny, info: &mut CallbackInfo) -> Update;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        assert_eq!(types[0].type_name, "OnClickCallback");
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 2);
                
                // First arg: &mut RefAny -> ty="RefAny", ref_kind=RefMut
                assert_eq!(args[0].ty, "RefAny", "First arg type should be base type without &mut");
                assert_eq!(args[0].ref_kind, RefKind::RefMut, "First arg should be RefMut");
                assert_eq!(args[0].name, Some("data".to_string()));
                
                // Second arg: &mut CallbackInfo -> ty="CallbackInfo", ref_kind=RefMut
                assert_eq!(args[1].ty, "CallbackInfo", "Second arg type should be base type without &mut");
                assert_eq!(args[1].ref_kind, RefKind::RefMut, "Second arg should be RefMut");
                assert_eq!(args[1].name, Some("info".to_string()));
                
                assert_eq!(returns.as_deref(), Some("Update"));
            }
            _ => panic!("Expected CallbackTypedef, got {:?}", types[0].kind),
        }
    }

    #[test]
    fn test_callback_typedef_with_ref() {
        // Test that &T is correctly parsed with RefKind::Ref and base type T
        let source = r#"
            pub type ReadCallback = extern "C" fn(state: &TextInputState) -> bool;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                
                // &TextInputState -> ty="TextInputState", ref_kind=Ref
                assert_eq!(args[0].ty, "TextInputState", "Arg type should be base type without &");
                assert_eq!(args[0].ref_kind, RefKind::Ref, "Arg should be Ref");
                
                assert_eq!(returns.as_deref(), Some("bool"));
            }
            _ => panic!("Expected CallbackTypedef"),
        }
    }

    #[test]
    fn test_callback_typedef_with_raw_pointer() {
        // Test that *mut T and *const T are correctly parsed as Value with full pointer type
        // Note: syn's to_token_stream().to_string() adds spaces, so we get "* mut" not "*mut"
        let source = r#"
            pub type DestructorCallback = extern "C" fn(ptr: *mut c_void);
            pub type CloneCallback = extern "C" fn(ptr: *const c_void) -> *const c_void;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 2);
        
        // DestructorCallback
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                
                // *mut c_void -> ty="*mut c_void", ref_kind=Value (pointers are passed by value)
                assert_eq!(args[0].ty, "*mut c_void", "Raw pointer should include the *mut prefix");
                assert_eq!(args[0].ref_kind, RefKind::Value, "Raw pointers are passed by value");
                
                assert!(returns.is_none());
            }
            _ => panic!("Expected CallbackTypedef for DestructorCallback"),
        }
        
        // CloneCallback
        match &types[1].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                
                // *const c_void -> ty="*const c_void", ref_kind=Value
                // Note: syn adds a space, but extract_ref_kind_from_syn_type reconstructs without space
                assert_eq!(args[0].ty, "*const c_void", "Raw pointer should include the *const prefix");
                assert_eq!(args[0].ref_kind, RefKind::Value, "Raw pointers are passed by value");
                
                // Return type comes from clean_type_string which may have spaces from syn
                let ret = returns.as_deref().unwrap();
                assert!(ret.contains("const") && ret.contains("c_void"), 
                    "Return should be *const c_void, got: {}", ret);
            }
            _ => panic!("Expected CallbackTypedef for CloneCallback"),
        }
    }

    #[test]
    fn test_callback_typedef_mixed_args() {
        // Test callback with mixed argument types: &mut, &, value, and raw pointers
        let source = r#"
            pub type MixedCallback = extern "C" fn(
                ref_mut_arg: &mut RefAny,
                ref_arg: &State,
                value_arg: usize,
                ptr_arg: *const Data
            ) -> Result;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 4);
                
                // &mut RefAny
                assert_eq!(args[0].ty, "RefAny");
                assert_eq!(args[0].ref_kind, RefKind::RefMut);
                
                // &State
                assert_eq!(args[1].ty, "State");
                assert_eq!(args[1].ref_kind, RefKind::Ref);
                
                // usize (value)
                assert_eq!(args[2].ty, "usize");
                assert_eq!(args[2].ref_kind, RefKind::Value);
                
                // *const Data (pointer, passed by value)
                assert_eq!(args[3].ty, "*const Data");
                assert_eq!(args[3].ref_kind, RefKind::Value);
                
                assert_eq!(returns.as_deref(), Some("Result"));
            }
            _ => panic!("Expected CallbackTypedef"),
        }
    }

    #[test]
    fn test_callback_typedef_no_return() {
        // Test callback with no return type (void)
        let source = r#"
            pub type VoidCallback = extern "C" fn(ptr: *mut Data);
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                assert!(returns.is_none(), "Void callbacks should have None as return");
            }
            _ => panic!("Expected CallbackTypedef"),
        }
    }

    #[test]
    fn test_simple_type_alias() {
        // Test that non-callback type aliases are extracted correctly
        let source = r#"
            pub type GLenum = u32;
            pub type MyVec = Vec<String>;
        "#;
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 2);
        
        match &types[0].kind {
            TypeDefKind::TypeAlias { target, .. } => {
                assert_eq!(target, "u32");
            }
            _ => panic!("Expected TypeAlias for GLenum"),
        }
        
        match &types[1].kind {
            TypeDefKind::TypeAlias { target, .. } => {
                assert_eq!(target, "Vec < String >");
            }
            _ => panic!("Expected TypeAlias for MyVec"),
        }
    }

    #[test]
    fn test_expands_impl_vec_macro() {
        let source = r#"
            pub struct Font { pub data: u8 }
            impl_vec!(Font, FontVec, FontVecDestructor);
        "#;
        let types = extract_types_from_source(source);
        
        // Should have: Font, FontVec, FontVecDestructor, FontVecDestructorType
        assert_eq!(types.len(), 4);
        
        let names: Vec<_> = types.iter().map(|t| t.type_name.as_str()).collect();
        assert!(names.contains(&"Font"));
        assert!(names.contains(&"FontVec"));
        assert!(names.contains(&"FontVecDestructor"));
        assert!(names.contains(&"FontVecDestructorType"));
    }

    #[test]
    fn test_expands_impl_option_macro() {
        let source = r#"
            pub struct Foo { pub x: i32 }
            impl_option!(Foo, OptionFoo, OptionFooEnumWrapper);
        "#;
        let types = extract_types_from_source(source);
        
        // Should have: Foo, OptionFoo, OptionFooEnumWrapper
        assert_eq!(types.len(), 3);
        
        let names: Vec<_> = types.iter().map(|t| t.type_name.as_str()).collect();
        assert!(names.contains(&"Foo"));
        assert!(names.contains(&"OptionFoo"));
        assert!(names.contains(&"OptionFooEnumWrapper"));
    }

    #[test]
    fn test_expands_impl_callback_macro() {
        let source = r#"
            pub type OnClickCallbackType = extern "C" fn(data: &mut RefAny) -> Update;
            impl_callback!(OnClick, OptionOnClick, OnClickCallback, OnClickCallbackType);
        "#;
        let types = extract_types_from_source(source);
        
        // Should have: OnClickCallbackType (type alias), OnClick, OptionOnClick, OnClickCallback
        assert_eq!(types.len(), 4);
        
        let names: Vec<_> = types.iter().map(|t| t.type_name.as_str()).collect();
        assert!(names.contains(&"OnClickCallbackType")); // The type alias
        assert!(names.contains(&"OnClick")); // CallbackWrapper
        assert!(names.contains(&"OptionOnClick")); // OptionCallbackWrapper
        assert!(names.contains(&"OnClickCallback")); // CallbackValue
    }

    #[test]
    fn test_is_primitive() {
        assert!(TypeIndex::is_primitive("u8"));
        assert!(TypeIndex::is_primitive("i32"));
        assert!(TypeIndex::is_primitive("f64"));
        assert!(TypeIndex::is_primitive("bool"));
        assert!(TypeIndex::is_primitive("*const u8"));
        assert!(TypeIndex::is_primitive("*mut c_void"));
        assert!(TypeIndex::is_primitive("&str"));
        assert!(TypeIndex::is_primitive("[u8; 4]"));
        
        assert!(!TypeIndex::is_primitive("String"));
        assert!(!TypeIndex::is_primitive("Vec"));
        assert!(!TypeIndex::is_primitive("FontCache"));
    }

    #[test]
    fn test_module_path_inference() {
        assert_eq!(
            infer_module_path("azul_core", Path::new("/foo/core/src/lib.rs")),
            ""
        );
        assert_eq!(
            infer_module_path("azul_core", Path::new("/foo/core/src/dom.rs")),
            "dom"
        );
        assert_eq!(
            infer_module_path("azul_core", Path::new("/foo/core/src/dom/mod.rs")),
            "dom"
        );
        assert_eq!(
            infer_module_path("azul_core", Path::new("/foo/core/src/widgets/button.rs")),
            "widgets::button"
        );
    }

    /// Comprehensive integration test that parses a realistic callbacks.rs file
    /// and verifies that callback_typedef types are correctly extracted.
    /// 
    /// This test reproduces the bug where `&mut RefAny` was being parsed as
    /// `type: "& mut RefAny", ref: "value"` instead of `type: "RefAny", ref: "refmut"`.
    /// 
    /// The test uses syn's AST directly and avoids string comparisons where possible.
    #[test]
    fn test_parse_realistic_callbacks_file() {
        // This source mirrors the structure of core/src/callbacks.rs
        let source = r#"
use core::ffi::c_void;

/// Specifies if the screen should be updated after the callback function has returned
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Update {
    DoNothing,
    RefreshDom,
    RefreshDomAllWindows,
}

/// RefAny is a type-erased reference-counted pointer
#[repr(C)]
pub struct RefAny {
    ptr: *mut c_void,
    sharing_info: u64,
}

/// Layout callback info passed to the layout function
#[repr(C)]
pub struct LayoutCallbackInfo {
    pub window_size: (u32, u32),
}

/// The styled DOM returned from layout callbacks
#[repr(C)]
pub struct StyledDom {
    pub root: u64,
}

/// IFrame callback return value
#[repr(C)]
pub struct IFrameCallbackReturn {
    pub dom: StyledDom,
}

/// Information passed to IFrame callbacks
#[repr(C)]
pub struct IFrameCallbackInfo {
    pub bounds: (f32, f32, f32, f32),
}

// ============================================================================
// CALLBACK TYPEDEFS - These are the critical types we need to parse correctly
// ============================================================================

/// Layout callback - takes &mut RefAny and &mut LayoutCallbackInfo
pub type LayoutCallbackType = extern "C" fn(&mut RefAny, &mut LayoutCallbackInfo) -> StyledDom;

/// Marshaled layout callback with extra marshal_data parameter
pub type MarshaledLayoutCallbackType = extern "C" fn(
    /* marshal_data */ &mut RefAny,
    /* app_data */ &mut RefAny,
    &mut LayoutCallbackInfo,
) -> StyledDom;

/// IFrame callback
pub type IFrameCallbackType =
    extern "C" fn(&mut RefAny, &mut IFrameCallbackInfo) -> IFrameCallbackReturn;

/// Simple callback with immutable reference
pub type OnTextInputCallback = extern "C" fn(&RefAny, &TextInputState) -> OnTextInputReturn;

/// Destructor callback using raw pointer
pub type RefAnyDestructorType = extern "C" fn(*mut c_void);

/// Clone callback using raw pointers
pub type RefCountCopyFnType = extern "C" fn(*const c_void) -> *const c_void;

/// Callback with no return value
pub type VoidCallback = extern "C" fn(&mut RefAny);

/// Callback returning a primitive
pub type BoolCallback = extern "C" fn(&RefAny) -> bool;

/// Text input state
#[repr(C)]
pub struct TextInputState {
    pub text: u64,
}

/// Text input return
#[repr(C)]
pub struct OnTextInputReturn {
    pub update: Update,
}
        "#;

        let types = extract_types_from_source(source);
        
        // Build a lookup map by type name for easier assertions
        let type_map: std::collections::HashMap<&str, &TypeDefinition> = types
            .iter()
            .map(|t| (t.type_name.as_str(), t))
            .collect();
        
        // ====================================================================
        // Verify struct extractions
        // ====================================================================
        
        // Update enum
        let update = type_map.get("Update").expect("Update enum not found");
        assert!(matches!(&update.kind, TypeDefKind::Enum { .. }), 
            "Update should be an Enum, got {:?}", update.kind);
        
        // RefAny struct
        let refany = type_map.get("RefAny").expect("RefAny struct not found");
        assert!(matches!(&refany.kind, TypeDefKind::Struct { .. }), 
            "RefAny should be a Struct, got {:?}", refany.kind);
        
        // ====================================================================
        // Verify callback_typedef extractions - THE CRITICAL PART
        // ====================================================================
        
        // LayoutCallbackType: fn(&mut RefAny, &mut LayoutCallbackInfo) -> StyledDom
        let layout_cb = type_map.get("LayoutCallbackType")
            .expect("LayoutCallbackType not found");
        
        match &layout_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 2, "LayoutCallbackType should have 2 args");
                
                // CRITICAL: First arg should be RefAny with RefMut, NOT "& mut RefAny" with Value
                assert_eq!(args[0].ref_kind, RefKind::RefMut, 
                    "First arg of LayoutCallbackType should be RefMut");
                assert!(!args[0].ty.contains('&'), 
                    "Type should not contain '&', got: {}", args[0].ty);
                assert!(args[0].ty.contains("RefAny"), 
                    "First arg base type should contain RefAny, got: {}", args[0].ty);
                
                // Second arg: &mut LayoutCallbackInfo
                assert_eq!(args[1].ref_kind, RefKind::RefMut,
                    "Second arg of LayoutCallbackType should be RefMut");
                assert!(!args[1].ty.contains('&'),
                    "Type should not contain '&', got: {}", args[1].ty);
                assert!(args[1].ty.contains("LayoutCallbackInfo"),
                    "Second arg base type should contain LayoutCallbackInfo, got: {}", args[1].ty);
                
                // Return type
                assert!(returns.is_some(), "LayoutCallbackType should have a return type");
                assert!(returns.as_ref().unwrap().contains("StyledDom"),
                    "Return type should contain StyledDom, got: {:?}", returns);
            }
            other => panic!("LayoutCallbackType should be CallbackTypedef, got {:?}", other),
        }
        
        // MarshaledLayoutCallbackType: fn(&mut RefAny, &mut RefAny, &mut LayoutCallbackInfo) -> StyledDom
        let marshaled_cb = type_map.get("MarshaledLayoutCallbackType")
            .expect("MarshaledLayoutCallbackType not found");
        
        match &marshaled_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 3, "MarshaledLayoutCallbackType should have 3 args");
                
                // All 3 args should be RefMut
                for (i, arg) in args.iter().enumerate() {
                    assert_eq!(arg.ref_kind, RefKind::RefMut,
                        "Arg {} of MarshaledLayoutCallbackType should be RefMut, got {:?}", i, arg.ref_kind);
                    assert!(!arg.ty.contains('&'),
                        "Arg {} type should not contain '&', got: {}", i, arg.ty);
                }
                
                assert!(returns.is_some());
            }
            other => panic!("MarshaledLayoutCallbackType should be CallbackTypedef, got {:?}", other),
        }
        
        // OnTextInputCallback: fn(&RefAny, &TextInputState) -> OnTextInputReturn
        // This tests immutable references (Ref, not RefMut)
        let text_input_cb = type_map.get("OnTextInputCallback")
            .expect("OnTextInputCallback not found");
        
        match &text_input_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 2, "OnTextInputCallback should have 2 args");
                
                // Both args should be Ref (immutable)
                assert_eq!(args[0].ref_kind, RefKind::Ref,
                    "First arg of OnTextInputCallback should be Ref, got {:?}", args[0].ref_kind);
                assert_eq!(args[1].ref_kind, RefKind::Ref,
                    "Second arg of OnTextInputCallback should be Ref, got {:?}", args[1].ref_kind);
                
                // Base types should not contain '&'
                assert!(!args[0].ty.contains('&'), "Type should not contain '&'");
                assert!(!args[1].ty.contains('&'), "Type should not contain '&'");
                
                assert!(returns.is_some());
            }
            other => panic!("OnTextInputCallback should be CallbackTypedef, got {:?}", other),
        }
        
        // RefAnyDestructorType: fn(*mut c_void)
        // This tests raw mutable pointer (passed by value)
        let destructor_cb = type_map.get("RefAnyDestructorType")
            .expect("RefAnyDestructorType not found");
        
        match &destructor_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1, "RefAnyDestructorType should have 1 arg");
                
                // *mut c_void should be Value (pointer passed by value)
                assert_eq!(args[0].ref_kind, RefKind::Value,
                    "Raw pointer arg should be Value, got {:?}", args[0].ref_kind);
                
                // The type should contain the full pointer type
                assert!(args[0].ty.contains("*") && args[0].ty.contains("mut"),
                    "Type should contain *mut, got: {}", args[0].ty);
                
                assert!(returns.is_none(), "RefAnyDestructorType should have no return type");
            }
            other => panic!("RefAnyDestructorType should be CallbackTypedef, got {:?}", other),
        }
        
        // RefCountCopyFnType: fn(*const c_void) -> *const c_void
        // This tests raw const pointer
        let clone_cb = type_map.get("RefCountCopyFnType")
            .expect("RefCountCopyFnType not found");
        
        match &clone_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1, "RefCountCopyFnType should have 1 arg");
                
                // *const c_void should be Value
                assert_eq!(args[0].ref_kind, RefKind::Value,
                    "Raw pointer arg should be Value, got {:?}", args[0].ref_kind);
                assert!(args[0].ty.contains("*") && args[0].ty.contains("const"),
                    "Type should contain *const, got: {}", args[0].ty);
                
                // Return type should also be a raw pointer
                assert!(returns.is_some());
                let ret = returns.as_ref().unwrap();
                assert!(ret.contains("*") && ret.contains("const"),
                    "Return type should contain *const, got: {}", ret);
            }
            other => panic!("RefCountCopyFnType should be CallbackTypedef, got {:?}", other),
        }
        
        // VoidCallback: fn(&mut RefAny)
        // Callback with no return
        let void_cb = type_map.get("VoidCallback")
            .expect("VoidCallback not found");
        
        match &void_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                assert_eq!(args[0].ref_kind, RefKind::RefMut);
                assert!(returns.is_none(), "VoidCallback should have no return");
            }
            other => panic!("VoidCallback should be CallbackTypedef, got {:?}", other),
        }
        
        // BoolCallback: fn(&RefAny) -> bool
        // Callback with immutable ref and primitive return
        let bool_cb = type_map.get("BoolCallback")
            .expect("BoolCallback not found");
        
        match &bool_cb.kind {
            TypeDefKind::CallbackTypedef { args, returns } => {
                assert_eq!(args.len(), 1);
                assert_eq!(args[0].ref_kind, RefKind::Ref, 
                    "BoolCallback arg should be Ref (immutable), got {:?}", args[0].ref_kind);
                assert!(returns.as_ref().map(|s| s.contains("bool")).unwrap_or(false),
                    "BoolCallback should return bool");
            }
            other => panic!("BoolCallback should be CallbackTypedef, got {:?}", other),
        }
    }

    /// Test that verifies the RefKind is correctly converted to/from string representation
    /// for JSON serialization in api.json
    #[test]
    fn test_refkind_to_json_representation() {
        // This test verifies that when we convert to JSON, we get the expected values
        
        let source = r#"
            pub type TestCallback = extern "C" fn(
                ref_arg: &Data,
                refmut_arg: &mut Data,
                value_arg: Data,
                ptr_arg: *const Data
            ) -> bool;
        "#;
        
        let types = extract_types_from_source(source);
        assert_eq!(types.len(), 1);
        
        match &types[0].kind {
            TypeDefKind::CallbackTypedef { args, .. } => {
                // ref_arg: &Data -> should become "ref" in JSON
                assert_eq!(args[0].ref_kind, RefKind::Ref);
                assert_eq!(args[0].ref_kind.as_str(), "ref");
                
                // refmut_arg: &mut Data -> should become "refmut" in JSON
                assert_eq!(args[1].ref_kind, RefKind::RefMut);
                assert_eq!(args[1].ref_kind.as_str(), "refmut");
                
                // value_arg: Data -> should become "value" in JSON
                assert_eq!(args[2].ref_kind, RefKind::Value);
                assert_eq!(args[2].ref_kind.as_str(), "value");
                
                // ptr_arg: *const Data -> should become "value" in JSON (pointer passed by value)
                assert_eq!(args[3].ref_kind, RefKind::Value);
                assert_eq!(args[3].ref_kind.as_str(), "value");
            }
            _ => panic!("Expected CallbackTypedef"),
        }
    }

    /// Test that parses the actual callbacks.rs file from the workspace
    /// to ensure our extraction works on real code
    #[test]
    fn test_parse_actual_callbacks_file() {
        let callbacks_path = Path::new(env!("CARGO_MANIFEST_DIR"))
            .parent()
            .unwrap()
            .join("core/src/callbacks.rs");
        
        if !callbacks_path.exists() {
            // Skip test if file doesn't exist (e.g., in CI without full repo)
            eprintln!("Skipping test_parse_actual_callbacks_file: {:?} not found", callbacks_path);
            return;
        }
        
        let content = std::fs::read_to_string(&callbacks_path)
            .expect("Failed to read callbacks.rs");
        
        let syntax_tree: syn::File = syn::parse_file(&content)
            .expect("Failed to parse callbacks.rs");
        
        let mut callback_typedefs = Vec::new();
        
        for item in &syntax_tree.items {
            if let syn::Item::Type(type_item) = item {
                if let syn::Type::BareFn(_) = &*type_item.ty {
                    if let Some(td) = extract_type_alias("azul_core", "callbacks", &callbacks_path, type_item) {
                        callback_typedefs.push(td);
                    }
                }
            }
        }
        
        // We should find at least some callback typedefs
        assert!(!callback_typedefs.is_empty(), 
            "Should find callback typedefs in callbacks.rs");
        
        // Verify that LayoutCallbackType is correctly parsed
        let layout_cb = callback_typedefs.iter()
            .find(|t| t.type_name == "LayoutCallbackType");
        
        if let Some(layout) = layout_cb {
            match &layout.kind {
                TypeDefKind::CallbackTypedef { args, .. } => {
                    // The first two args should be &mut references
                    assert!(args.len() >= 2, "LayoutCallbackType should have at least 2 args");
                    
                    // CRITICAL: Verify the bug is fixed
                    // args[0] should be RefMut with type "RefAny", NOT Value with type "& mut RefAny"
                    assert_eq!(args[0].ref_kind, RefKind::RefMut,
                        "LayoutCallbackType first arg should be RefMut, got {:?}", args[0].ref_kind);
                    assert!(!args[0].ty.starts_with("&"),
                        "Type should not start with '&', got: {}", args[0].ty);
                    
                    assert_eq!(args[1].ref_kind, RefKind::RefMut,
                        "LayoutCallbackType second arg should be RefMut, got {:?}", args[1].ref_kind);
                    assert!(!args[1].ty.starts_with("&"),
                        "Type should not start with '&', got: {}", args[1].ty);
                    
                    eprintln!("✓ LayoutCallbackType correctly parsed:");
                    eprintln!("  arg[0]: type={:?}, ref_kind={:?}", args[0].ty, args[0].ref_kind);
                    eprintln!("  arg[1]: type={:?}, ref_kind={:?}", args[1].ty, args[1].ref_kind);
                }
                _ => panic!("LayoutCallbackType should be CallbackTypedef"),
            }
        }
        
        eprintln!("Found {} callback typedefs in callbacks.rs", callback_typedefs.len());
        for cb in &callback_typedefs {
            eprintln!("  - {}", cb.type_name);
        }
    }
}
